package com.baofu.paycore.service.processor.consume;

import com.baofu.clear.service.facade.enums.EventTypeEnum;
import com.baofu.paycore.biz.external.clear.EventService;
import com.baofu.paycore.biz.external.clear.model.EventRuleResBO;
import com.baofu.paycore.common.enums.FundTypeEnum;
import com.baofu.paycore.service.facade.enums.*;
import com.baofu.paycore.common.utils.Constant;
import com.baofu.paycore.manager.model.PayBO;
import com.baofu.paycore.service.converter.DTO2BOConverter;
import com.baofu.paycore.service.processor.PayBaseProcessor;
import com.google.common.base.Objects;
import com.baofu.paycore.manager.model.SubPayBO;
import com.baofu.paycore.service.facade.model.pay.PayDTO;
import com.baofu.paycore.service.facade.model.pay.FundDetailInfoDTO;
import com.google.common.collect.Lists;
import com.system.commons.exception.BizServiceException;
import com.system.commons.sequence.redis.SequenceFacade;
import com.system.commons.utils.BeanCopyUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * 资金单处理类
 *
 * <p/>
 *   1、构建支付属性
 * </p>
 * User: LZQ Date: 2016/04/20  Version: 1.0
 */
@Slf4j
@Component
public class ConsumeFundProcessor implements PayBaseProcessor<PayBO,PayDTO> {

    /**
     * 资金单明细NO
     */
    @Autowired
    private SequenceFacade sequenceFacade;

    /**
     * 会计事件服务
     */
    @Autowired
    private EventService eventService;

    /**
     * 构建支付属性
     *
     * @param payBO      支付模型
     * @param payDTO     请求参数
     */
    @Override
    public PayBaseProcessor process(PayBO payBO, PayDTO payDTO) {

        //付款方
        List<FundDetailInfoDTO> list = payDTO.getPayerFundDetails();
        //查询会计事件
        String eventNo = eventProcessor(list, payDTO);
        //收款方
        FundDetailInfoDTO payeeFund = payDTO.getPayeeFundDetail();
        List<SubPayBO> subPayBOs = Lists.newArrayList();
        for (FundDetailInfoDTO fundDetailInfoDTO : list) {
            if(PayToolRouteEnum.CLEAR_WEBPAY_ROUTE.containsPayTool(fundDetailInfoDTO.getPayToolType())){
                //支付子单
                List<SubPayBO> bankPay = this.buildSubPayByBank(fundDetailInfoDTO, payeeFund.getAccountNo(),
                        payDTO.getPayerAccNo(), eventNo);
                subPayBOs.addAll(bankPay);
            }else  if(PayToolRouteEnum.ACCOUNT_TRANSFER_ROUTE.containsPayTool(fundDetailInfoDTO.getPayToolType())){
                //余额子单
                List<SubPayBO> balancePay = this.buildSubPayByBalance(fundDetailInfoDTO, payeeFund.getAccountNo(),
                        payDTO.getPayerAccNo(), eventNo);
                subPayBOs.addAll(balancePay);
            }else{
                log.error("暂不支付此支付工具类型:{}", fundDetailInfoDTO.getPayToolType());

            }
        }
        payBO.setSubPayList(subPayBOs);
        return this;
    }

    /**
     * 有手续费会计事件处理流程
     *
     * @param list  资金单集合
     * @return      事件号
     */
    private String eventProcessor(List<FundDetailInfoDTO> list, PayDTO payDTO){
        long chargeAmt = 0;
        for (FundDetailInfoDTO fundDetailInfoDTO : list){
            chargeAmt += Constant.validateLong(fundDetailInfoDTO.getBizChargeAmount());
        }
        if (chargeAmt > 0){
            EventRuleResBO eventRuleResBO = eventService.queryEventNo(EventTypeEnum.FEE, payDTO.getSubBizType());
            if (!PayStatusEnums.SUCCESS.getCode().equals(eventRuleResBO.getStatus())) {
                log.error("会计事件获取失败:{}",payDTO.getBizType());
                throw new BizServiceException(PayCoreErrorCode.EVENT_NO_NOT_EXIST, eventRuleResBO.getErrorMsg());
            }
            return eventRuleResBO.getEventNo();
        }
        return null;
    }
    /**
     * 封装银行卡支付资金单
     *
     * @param fundDetailInfoDTO     资金单
     * @param payeeAccNo            收款方
     * @param payerAccNo            付款方
     * @return                      支付子资金单集合
     */
    private List<SubPayBO> buildSubPayByBank(FundDetailInfoDTO fundDetailInfoDTO, String payeeAccNo, String payerAccNo,
                                             String eventNo) {
        List<SubPayBO> list = new ArrayList<>();

        //没有手续费
        if (Constant.validateLong(fundDetailInfoDTO.getBizChargeAmount()) <= 0) {

            fundDetailInfoDTO.setBizChargeAmount(fundDetailInfoDTO.getPayToolAmount());
            return this.buildSubPayBO(fundDetailInfoDTO, FundTypeEnum.PAY.getCode(), payeeAccNo,
                    payerAccNo, null);
        }
        //支付金额转换
        long payToolAmount = Constant.validateLong(fundDetailInfoDTO.getPayToolAmount());
        long bizChargeAmount = Constant.validateLong(fundDetailInfoDTO.getBizChargeAmount());
        fundDetailInfoDTO.setBizChargeAmount(fundDetailInfoDTO.getPayToolAmount());
        //支付子单
        SubPayBO subPayByPay = getSubPayBO(fundDetailInfoDTO, payeeAccNo);
        list.add(subPayByPay);
        //手续费单
        list.add(buildBankPay(subPayByPay, fundDetailInfoDTO.getBizFeeAccount(), FundTypeEnum.FEE, null,
                 payToolAmount, bizChargeAmount, eventNo));
        //转账单
        list.add(buildBankPay(subPayByPay, payerAccNo, FundTypeEnum.RECHARGE_TRANSFER, payeeAccNo,
                payToolAmount, payToolAmount - bizChargeAmount, null));

        return list;
    }

    /**
     * 获取支付子单
     *
     * @param fundDetailInfoDTO     资金单明细对象
     * @return                      支付子单
     */
    private SubPayBO getSubPayBO(FundDetailInfoDTO fundDetailInfoDTO, String payeeAccNo){
        SubPayBO subPayByPay = DTO2BOConverter.subPayDTOToBO(fundDetailInfoDTO);
        subPayByPay.setFundType(FundTypeEnum.PAY.getCode());
        subPayByPay.setPayeeAccNo(payeeAccNo);
        initFundDetail(subPayByPay);
        return subPayByPay;
    }

    /**
     * 封装余额支付资金单
     *
     * @param fundDetailInfoDTO     资金单信息
     * @param payeeAccNo            收款方
     * @param payerAccNo            付款方
     * @return                      资金单
     */
    private List<SubPayBO> buildSubPayByBalance(FundDetailInfoDTO fundDetailInfoDTO, String payeeAccNo,
                                                String payerAccNo, String eventNo) {

        long bizChargeAmount = Constant.validateLong(fundDetailInfoDTO.getBizChargeAmount());
        //存在手续费
        if (bizChargeAmount <= 0) {
            //没有手续费
            fundDetailInfoDTO.setBizChargeAmount(fundDetailInfoDTO.getPayToolAmount());
            return buildSubPayBO(fundDetailInfoDTO, FundTypeEnum.PAY.getCode(), payeeAccNo,
                    payerAccNo, null);

        }
        //子资金单
        fundDetailInfoDTO.setBizChargeAmount(fundDetailInfoDTO.getPayToolAmount());
        List<SubPayBO> subPayByBal = buildSubPayBO(fundDetailInfoDTO, FundTypeEnum.PAY.getCode(), payeeAccNo,
                 payerAccNo, null);
        List<SubPayBO> list = new ArrayList<>();
        list.addAll(subPayByBal);

        //手续费资金单
        fundDetailInfoDTO.setBizChargeAmount(bizChargeAmount);
        List<SubPayBO> subPayByFee = buildSubPayBO(fundDetailInfoDTO, FundTypeEnum.FEE.getCode(), null,
                 fundDetailInfoDTO.getBizFeeAccount(), eventNo);
        list.addAll(subPayByFee);

        return list;
    }

    /**
     * 组装子资金单
     *
     * @param fundDetailInfoDTO 资金单信息
     * @param fundType          资金单类型
     * @param account           收款方账号
     * @param payerAccNo        付款方账号
     * @return                  子资金单
     */
    private List<SubPayBO> buildSubPayBO(FundDetailInfoDTO fundDetailInfoDTO, String fundType,
                                         String account, String payerAccNo,String eventNo) {
        List<SubPayBO> list = new ArrayList<>();
        SubPayBO subPayBO = DTO2BOConverter.subPayDTOToBO(fundDetailInfoDTO);
        subPayBO.setFundType(fundType);
        subPayBO.setPayeeAccNo(account);
        subPayBO.setPayerAccNo(payerAccNo);
        subPayBO.setEventNo(eventNo);
        initFundDetail(subPayBO);
        if (PayToolRouteEnum.CLEAR_WEBPAY_ROUTE.containsPayTool(subPayBO.getPayToolType())) {
            list.add(buildBankPay(subPayBO, payerAccNo, FundTypeEnum.RECHARGE_TRANSFER, account,
                    fundDetailInfoDTO.getPayToolAmount(), fundDetailInfoDTO.getBizChargeAmount(),eventNo));
        }
        list.add(subPayBO);
        return list;
    }

    /**
     * 初始化子单对象
     *
     * @param subPayBO      子单对象
     */
    private void initFundDetail(SubPayBO subPayBO) {
        subPayBO.setFundDetailNo(sequenceFacade.getUniqueSeq());
        subPayBO.setFreezeNo(sequenceFacade.getUniqueSeq());
        subPayBO.setStatus(PayStatusEnums.INIT.getCode());
    }

    /**
     * 银行卡充值冻结后 解冻转账
     *
     * @param subPay         付款是银行卡
     * @return               资金单
     */
    private SubPayBO buildBankPay(SubPayBO subPay, String payerAccNo, FundTypeEnum fundType,
                                  String accountNo, Long payAmt, Long reqAccAmt, String eventNo) {
        SubPayBO subPayBO = BeanCopyUtil.objConvert(subPay, SubPayBO.class);
        log.debug("创建转账单:{}", subPayBO);
        subPayBO.setFundType(fundType.getCode());
        subPayBO.setPayToolType(PayToolTypeEnum.ACCOUNT.getCode());
        subPayBO.setPayToolSubType(PayToolSubTypeEnum.ACCOUNT.getSubPayToolCode());
        subPayBO.setClassType(subPay.getClassType());
        subPayBO.setPayToolAmt(payAmt);
        subPayBO.setFundAmt(reqAccAmt);
        subPayBO.setEventNo(eventNo);
        subPayBO.setRelateFundDetailNo(subPay.getFundDetailNo());
        //付款方 余额
        subPayBO.setPayerAccNo(payerAccNo);
        //收款方 商户
        subPayBO.setPayeeAccNo(accountNo);
        initFundDetail(subPayBO);
        return subPayBO;
    }
}